//
// gcs.cpp
// gcs
// Grand Central Station, controlling process of the Protea Server / contains global definitions and main entry point
// Copyright (C) 2003, 2004, 2005 eXegeSys, Inc.
// Copyright (C) 2008 Bruce A. James
//
// This program is part of the Protea Project.
//
// The Protea Project is free software; you can redistribute it and/or modify it 
// under the terms of the GNU General Public License as published by the 
// Free Software Foundation, either version 3 of the License, or (at your 
// option) any later version.
//
// The Protea Project is distributed in the hope that it will be useful, but 
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
// for more details.
// 
// You should have received a copy of the GNU General Public License along 
// with this program.  If not, see <http://www.gnu.org/licenses/>.
// 
// Please send correspondence to:
// 
// theproteaproject@gmail.com
//

#include "../SDK/ToolsInterface.h"
#include "../AppHandler/parms_xml.h"
#include <signal.h>
#include <locale.h>
#include <string.h>
#include "gcs.h"
#include "../entry.h"

// globals

DAEMON_SERVICE( XCGCS, gcs, "GCS", "Protea GCS", "Supports the Protea Project distributed services", NULL );
XCServiceDispatcher service_daemon;

char* new_listen_port;	// contains the changed listening socket number
bool booted = false;
bool running_as_service = false;



//////////////////////////////////////////////////////////////////////////////
//
// XCProcessStats Class
//
XCProcessStats::XCProcessStats()
{
	pid = 0;
	trans_complete = 0;
	trans_open = 0;
	trans_stated = 0;
	listenport = 0;
	parentpid = 0;

	last_check = 0;

	msg_sent = 0;
	msg_recv = 0;

	cpu_time = 0;
	start_time = 0;

	debug_type_filter = 0;
	memset( debug_level_cutoff, 0, sizeof( debug_level_cutoff ) );

	filename = NULL;
	host = NULL;
};

XCProcessStats::~XCProcessStats() {
	if (filename)
		delete [] filename;
};





//
// XCSubstations Class
//
XCSubstation::XCSubstation( const char* name, XCMsgPortPtr port )
{
	hostname = strdup( name );
	printf( "Add host: %s\n", hostname );
	host = hostname;
	filename = "substation";

	msg_port = port;
	port->extradata = (void*)this;
	applist = NULL;
	next = NULL;
};


XCSubstation::~XCSubstation()
{
	if (hostname)
		free( hostname );
};



XCApplication* XCSubstation::FindApplication( int pid ) {
	// Is it a child application?
	XCApplication* temp = applist;
	while (temp) {
		if (temp->pid == pid)
			return temp;
		temp = temp->next_in_sub_list;
	};
	return NULL;
};


void XCSubstation::RemoveApplication( XCApplication* app ) {
	if (applist == app) {
		// First one in the list
		applist = app->next_in_sub_list;
	} else {
		XCApplication* temp = applist;
		while (temp->next_in_sub_list && temp->next_in_sub_list != app) {
			temp = temp->next_in_sub_list;
		};

		if (temp->next_in_sub_list)
			temp->next_in_sub_list = app->next_in_sub_list;
	};
};


XCApplication* XCSubstation::AddApplication( startup_message* start_msg ) {
//	printf( "Add application: %d\n", start_msg->appnum );
	if (start_msg->appnum > 0) {
		XCApplication* newapp = new XCApplication();

		// Register application with the substation
		newapp->number = start_msg->appnum;
		newapp->pid = start_msg->pid;
		newapp->parentpid = pid;
		newapp->owner = this;
		newapp->host = host;
		newapp->next_in_sub_list = applist;

		newapp->filename = new char[ start_msg->filelen + 1 ];
		memcpy( newapp->filename, start_msg->data, start_msg->filelen );
		newapp->filename[start_msg->filelen] = 0;

		applist = newapp;

		return newapp;
	} else
		return NULL;
};



// Find the PID owner
XCProcessStats* XCSubstation::FindStatusPID( status_message* statmsg ) {
//	printf( "Find PID: %d\n", statmsg->pid );
	return FindPID( statmsg->pid );
};



XCProcessStats* XCSubstation::FindPID( long pid_find ) {
	// Is it the substation?
	if (pid == pid_find)
		return this;

	// Is it a child application?
	XCApplication* temp = applist;
	while (temp) {
		if (temp->pid == pid_find)
			return temp;
		temp = temp->next_in_sub_list;
	};

	// Did something go wrong?
	return NULL;
};



XCInterface::XCInterface( startup_message* start_msg, XCMsgPortPtr port ) {
	pid = start_msg->pid;

	host = strdup( start_msg->data + start_msg->filelen );

	int len = start_msg->filelen;
	filename = new char[ len + 1 ];
	memcpy( filename, start_msg->data, len );
	filename[len] = 0;

	msg_port = port;
	port->extradata = (void*)this;

	next = NULL;
};



XCInterface::~XCInterface() {
	free( host );

	filename = NULL;
};



//
// XCAppInterface class
//
long XCApplication::LoadBalanceScore() {
	int value;
	value = rand() % 100;
//	cout << "Load balance score: " << value << "  Host:" << host << "  Num:" << number << endl;
	return value;
};





//
// XCGCSTransactionList
//
XCGCSTransactionList::XCGCSTransactionList() {
	transactions = NULL;
};


XCGCSTransaction* XCGCSTransactionList::Find( long id ) {
	XCGCSTransaction* temp = transactions;

	// Attempt to locate transaction
	while (temp && temp->id != id)
		temp = temp->next;

	return temp;
};



XCGCSTransaction* XCGCSTransactionList::Add( long id ) {
	XCGCSTransaction* temp = new XCGCSTransaction;
	temp->id = id;
	temp->host = NULL;

	temp->next = transactions;
	transactions = temp;

	return temp;
};



void XCGCSTransactionList::Remove( long id ) {
	XCGCSTransaction* ttemp = transactions;

	if (ttemp) {
		// Is this the first record?
		if (ttemp->id == id) {
			transactions = ttemp->next;
			delete ttemp;
		} else {
			while (ttemp->next) {
				if (ttemp->next->id == id) {
					XCGCSTransaction* deleteme = ttemp->next;
					ttemp->next = deleteme->next;
					delete deleteme;
					return;
				};
				ttemp = ttemp->next;
			};
		};
	};
};


void XCGCSTransactionList::RemoveApp( XCApplication* app ) {
	XCGCSTransaction* ttemp = transactions;

	printf( "Remove transaction records for app: %s\n", app->filename );

	// Delete records off the front
	while (ttemp && ttemp->host == app) {
		transactions = ttemp->next;
		delete ttemp;
		ttemp = transactions;
	};

	if (ttemp) {
		// Delete records in the middle
		while (ttemp->next) {
			if (ttemp->next->host == app) {
				XCGCSTransaction* deleteme = ttemp->next;
				ttemp->next = deleteme->next;
				delete deleteme;
			};
			ttemp = ttemp->next;
		};
	};
};



//
// XCSubstationList class
//
XCSubstationList::XCSubstationList() {
	hosts = NULL;
	interfaces = NULL;
};

XCSubstationList::~XCSubstationList() {
	XCSubstation* deleteme;
	while (hosts)
	{
		deleteme = hosts;
		hosts = hosts->next;
		delete deleteme;
	};

	XCInterface* deleteme2;
	while (interfaces)
	{
		deleteme2 = interfaces;
		interfaces = interfaces->next;
		delete deleteme2;
	};
};

XCSubstation* XCSubstationList::AddHost( startup_message* startmsg, XCMsgPortPtr port ) {
	crit.Lock();

	XCSubstation* temphost = hosts;
	const char* hostname = startmsg->data + startmsg->filelen;
	while (temphost && XCStrCaseCompare( temphost->GetHostName(), hostname ) != 0) {
		temphost = temphost->next;
	};

	if (!temphost && port) {
		temphost = new XCSubstation( hostname, port );
		temphost->next = hosts;
		temphost->pid = startmsg->pid;

		temphost->filename = new char[ startmsg->filelen + 1 ];
		memcpy( temphost->filename, startmsg->data, startmsg->filelen );
		temphost->filename[startmsg->filelen] = 0;

//		printf( "SUBSTATION PID: %d\n", temphost->pid );
		hosts = temphost;
	};

	crit.UnLock();
//	printf( "FindHost: %s, %s\n", hostname, temphost ? "True" : "False" );
	return temphost;
};


XCSubstation* XCSubstationList::FindHost( const char* hostname ) {
	crit.Lock();
	XCSubstation* temphost = hosts;
	while (temphost && XCStrCaseCompare( temphost->GetHostName(), hostname ) != 0) {
		temphost = temphost->next;
	};
	crit.UnLock();

	return temphost;
};

XCSubstation* XCSubstationList::FindHost( XCMsgPortPtr port ) {
	crit.Lock();
	XCSubstation* temphost = hosts;
	while (temphost && temphost->msg_port != port) {
		temphost = temphost->next;
	};
	crit.UnLock();

	return temphost;
};

void XCSubstationList::RemoveHost( XCSubstation* substation ) {
	crit.Lock();

	XCSubstation* temphost = hosts;
	if (temphost == substation)
		hosts = temphost->next;
	else {
		while (temphost->next && temphost->next != substation) {
			temphost = temphost->next;
		};

		if (temphost->next) {
			XCSubstation* save = temphost->next;
			temphost->next = temphost->next->next;
			temphost = save;
		} else
			temphost = NULL;
	};

	// Unregister the application
	XCApplication* apps = temphost->applist;
	while (apps) {
		RemoveApplication( apps );
		apps = apps->next_in_sub_list;
	};

	crit.UnLock();

	if (temphost) {
		delete temphost;
	};
};

void XCSubstationList::AddApplication( startup_message* start_msg, XCMsgPortPtr port ) {
	// Link the application with the substation
	XCSubstation* host = (XCSubstation*)port->extradata;
	XCApplication *app = host->AddApplication( start_msg );

	cout << "Add application " << start_msg->appnum << " to host " << host->host << endl;
	// Link the application in the application list
	XCApplicationList* temp = applist;
	while (temp && temp->number != start_msg->appnum) {
		temp = temp->next;
	};

	if (!temp) {
		temp = new XCApplicationList;
		temp->next = applist;
		temp->first_app = NULL;
		temp->number = start_msg->appnum;
		applist = temp;
	};

	// Register application with the application list
	app->next_in_app_list = temp->first_app;
	temp->first_app = app;
};




void XCSubstationList::RemoveApplication( XCMsgPort* host, int pid ) {
	XCSubstation* sub = FindHost( host );
	XCApplication* app = sub->FindApplication( pid );

	// Did we find an app?  Or are we trying to remove an application that isn't registered?
	if (app) {
		cout << "Remove application " << app->number << " from host " << app->host << "  pid=" << app->pid << endl;
		sub->RemoveApplication( app );
		RemoveApplication( app );
		delete app;
	};
};



void XCSubstationList::RemoveApplication( XCApplication* app  ) {
	// Find the application in the application list
	XCApplicationList* temp = applist;
	while (temp && temp->number != app->number) {
		temp = temp->next;
	};

	if (temp && temp->first_app) {
		if (temp->first_app == app) {
			temp->first_app = app->next_in_app_list;
		} else {
			XCApplication* findme = temp->first_app;
			while (findme->next_in_app_list && findme->next_in_app_list != app)
				findme = findme->next_in_app_list;

			if (findme->next_in_app_list == app) {
				findme->next_in_app_list = app->next_in_app_list;
			};
		};
	};

	// Remove any transaction references to this host...
	translist.RemoveApp( app );
};



XCApplication* XCSubstationList::LoadBalance( int appnum ) {
	INFUNC( XCSubstationList::LoadBalance, NULL );

	XCApplicationList* temp = applist;
	while (temp && temp->number != appnum) {
		temp = temp->next;
	};

	if (!temp)
		return NULL;

	long best_score = -1;
	XCApplication* best_app = temp->first_app;
	XCApplication* tempapp = temp->first_app;
	while (tempapp) {
		long score = tempapp->LoadBalanceScore();
		if (score > best_score) {
			best_score = score;
			best_app = tempapp;
		};

		tempapp = tempapp->next_in_app_list;
	};

	OUTFUNCRET( best_app );
};


void XCSubstationList::AddInterface( startup_message* start_msg, XCMsgPortPtr port ) {
	crit.Lock();

	XCInterface* tempint = new XCInterface( start_msg, port );
	tempint->next = interfaces;
	interfaces = tempint;

	crit.UnLock();
};


void XCSubstationList::RemoveInterface( XCInterface* todelete ) {
	crit.Lock();

	XCInterface* tempint = interfaces;
	if (tempint == todelete)
		interfaces = interfaces->next;
	else {
		while (tempint->next && tempint->next != todelete)
			tempint = tempint->next;

		if (tempint->next)
			tempint = tempint->next;
		else
			tempint = NULL;
	};
	crit.UnLock();

	if (tempint) {
		delete tempint;
	};
};


void XCSubstationList::Shutdown() {
/*
	// ***** TEMPORARY *****
	// Trace out the status of each process in the system
	XCProcess* tempproc = processlist;
	ToolsTrace( TRC_MESSAGING, TLV_HIGH, "PID\tHOSTNAME\tFILENAME\tLISTENER\tPARENT" );
	ToolsTrace( TRC_MESSAGING, TLV_HIGH, "---\t--------\t--------\t--------\t------" );
	while ( tempproc )
	{
		ToolsTrace( TRC_MESSAGING, TLV_HIGH, "%ld\t%s\t%s\t%ld\t%ld", tempproc->pid, tempproc->GetHostname(), tempproc->GetFilename(), tempproc->listenport, tempproc->parentpid );
		tempproc = tempproc->next;
	}
	ToolsTrace( TRC_MESSAGING, TLV_HIGH, "---\t--------\t--------\t--------\t------" );
	// ***** TEMPORARY *****

	// send the message along...
	// broadcast
	if (msg->GetHeaderPtr()->miscflag | misc_broadcast)
	{
		ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "*************************" );
		ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "System is going down NOW!" );
		ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "*************************" );
		AllowUI = false;

		tempport = childlist;
		while( tempport != NULL )
		{
			// don't tell the murderer that i'm murdering everyone else!
			if ( tempport == port )
			{
				murderer = tempport;
				murderer->ready = false;
			}
			else
				SendMessage( msg_src, tempport, msg );	// murder everyone!
			tempport = tempport->next;
		};
		if (parent && parent == port )
		{
			murderer = parent;
			murderer->ready = false;
		};
	}
	// individual object
	else
	{

	};
*/
};







//
// XCGCS Class
//
// constructor
XCGCS::XCGCS( char* name, char* dispname, char* desc, void* func, char* deps ) : XCService( name, dispname, desc, func, deps )
{
	AllowUI = false;
	murderer = NULL;
	shutdown = false;
};

// destructor
XCGCS::~XCGCS()
{
};


// Catch the exit process of all children...
void catchchildren( int signum ) {
	XCWaitChildren( &gcs, signum );
};

void XCGCS::StartUp()
{
	if (!running_as_service) {
		char* buffer = new char[ FILENAME_MAX ];
		char* hostname = new char[ FILENAME_MAX ];
		char** parms = new char*[5];

	//	strcpy( hostname, "localhost" );
		XCGetHostName( hostname, FILENAME_MAX );

		// setting up parameters to start a new substation
		char temp[ 20 ];
		sprintf( temp, "%ld", gcs.port );
		sprintf( buffer, "substation" );
		parms[0] = hostname;	// host name
		parms[1] = temp;		// gcs listener port
		parms[2] = NULL;		// terminate the list...

		ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "GCS Create Process : %s", buffer );

		app_start_message message;
		memset( &message, 0, sizeof( app_start_message ) );

		message.appl_nbr = 0;
		strcpy( message.appl_name, "substation" );
		for (int i=0; i<num_tc; i++)
			message.debug_level_cutoff[i] = gcs.debug_level_cutoff[i];
		message.debug_type_filter = gcs.debug_type_filter;
		message.authentication_method = gcs.auth_method;
		if (gcs.auth_parameter) {
			message.authentication_parm_len = strlen( gcs.auth_parameter );
			memcpy( message.data, gcs.auth_parameter, message.authentication_parm_len );
		};
		message.worker_threads = gcs.worker_threads;

		XCMsgPortPtr subchild = CreateChild( buffer, 2, parms, true, &message );

		// Set the childs port type
		subchild->porttype = pt_sub;

		// Cleanup
		delete [] buffer;
		delete [] parms;
		delete [] hostname;
	} else {
		// Use the services manager to start it
		service_daemon.StartRemoteService( NULL, "substation" );
	};
};




bool XCGCS::LogAdminMsg( admin_message* admin ) {
	// save the message to the admin message log file
	admin_log.open( ADMIN_LOG_FILENAME, ios::out | ios::app ); 
	admin_log << "TMS:" << XCGetSystemTime() << ",ERR#: " << admin->err_num << ",FLD#" << admin->field_num << ",PID#" << admin->src_pid << ",MSG:" << admin->err_message << endl;
	admin_log.close();
	return true;
};







bool XCGCS::HandleStatusMsg( status_message* statmsg, XCMsgPortPtr port ) {
	// process status information
	ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "Status Message Received from pid : %d", statmsg->pid );

	XCSubstation* host = (XCSubstation*)port->extradata;
	if (host) {
		XCProcessStats* stats = host->FindStatusPID( statmsg );
		if (stats) {
			stats->trans_complete = statmsg->trans_complete;
			stats->trans_open = statmsg->trans_open;
			stats->trans_stated = statmsg->trans_stated;
			stats->msg_recv = statmsg->msg_recv;
			stats->msg_sent = statmsg->msg_sent;
			stats->last_check = timer.GetCurrent();
			stats->debug_type_filter = statmsg->debug_type_filter;
			for (int i=0; i<num_tc; i++)
				stats->debug_level_cutoff[i] = statmsg->debug_level_cutoff[i];
		};
	};


/*
	// find correct XCProcess object in process list
	XCProcess* found = processlist.Find( statmsg->pid );
	if (found) {
		if (!statmsg->start ) {
			processlist.Delete( found );
			found = NULL;
		};
	} else {
		if (statmsg->start) {
			found = processlist.Add( statmsg->pid );
			found->listenport = statmsg->listenport;
			found->parentpid = statmsg->parentpid;
			found->SetFilename( statmsg->filename );
//			found->SetHostname( statmsg->hostname );
		} else
			ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "Attempt to remove application not it the list" );
	};
*/

	if (!booted) {
		StartInterfaces();
		booted = true;
	};

	return true;
};





bool XCGCS::HandleDispatchMessage( XCMsgPort* port, XCToolsMessage* msg ) {
	INFUNC( XCGCS::HandleDispatchMessage, this );

	XCMsgPortPtr tempport;
	XCMsgPortPtr outport = NULL;
	XCToolsMessage msg_send( this );

	bool retval = true;
	bool all_gone;

	disp_message* dispmsg = (disp_message*)msg->GetDataPtr();
	startup_message* start_msg;
	int c;
	bool found;
	long temphostnum;

	ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "Dispatch Message Opcode : %ld" , dispmsg->opcode );
	switch( dispmsg->opcode )
	{
	// someone just told GCS to start a new application (via substation and dispatcher)
	case disp_start_app:
		ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "GCS Starting application %s", dispmsg->data );
		c = 0;
		found = false;
		temphostnum = 0;
		break;

	// someone wants information from us!
	case disp_query_info:
		ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "GCS received query for info : %s", dispmsg->data );

		// is source gcs? if so, then we are starting the system
		if ( msg->GetHeaderPtr()->sdflag & src_gcs )
		{
			// send load balancing information back to substation
			// ... should probably be a disp_query_response ...
		}
		// else - do nothing yet!
		break;

	// someone just said "Hello!  I'm ready to start processing!"
	case disp_hello:
		start_msg = (startup_message*)dispmsg->data;

		// Register this application
		if (start_msg->appnum == -1) {
			// Add a new host
			hostlist.AddHost( start_msg, port );

			// set port type to substation
			port->porttype = pt_sub;
		} else
			hostlist.AddApplication( start_msg, port );

		// register port as ready...
		port->ready = true;
		break;

	case disp_goodbye: 
		start_msg = (startup_message*)dispmsg->data;
		hostlist.RemoveApplication( port, start_msg->pid );
		break;

	// A new User Interface just said "Hello!"  Direct a dispatcher to contact the UI
	case disp_contact_ui:
		start_msg = (startup_message*)dispmsg->data;
		hostlist.AddInterface( start_msg, port );
		port->porttype = pt_ui;
		break;

	case disp_shutdown:
//		hostlist.Shutdown();
		break;

	case disp_shutdown_confirm:
		if (msg->GetHeaderPtr()->miscflag & misc_broadcast)
		{
			all_gone = true;	// check if all our children are gone!!!
			port->ready = false;
			port->transport->CancelIO();

			// Was that the last child?
			if (!childlist)
			{
				if (murderer)
				{
					ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "All children are gone - murderer : %ld", (long)murderer->transport->GetInHandle() );
					msg_send.CreateMsg( DISP_MESSAGE_SIZE );
					msg_send.SetHeader( msg_dispatch, src_app, dest_gcs, true, false);
					dispmsg = (disp_message*)msg_send.GetDataPtr();
					dispmsg->opcode = disp_shutdown_confirm;
					SendMessage( msg_src, murderer, &msg_send );
					msg_send.DeleteMsg();
					murderer->transport->Close();
				}
				else
				{
					ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "No murderer set" );
				};
				ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "GCS Stopping Now" );
				QuitLoop();
			};
			retval = false;
		}
		else
		{
			ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "A child just exited..." );	
			outport = NULL;
			tempport = childlist;
			while (tempport)
			{
				if ( tempport == port )
				{
					tempport->ready = false;
					tempport->transport->Close();
					// delete XCMsgPortPtr
					if (outport)
					{
						outport->next = tempport->next;
						tempport->Release();
						tempport = outport->next;
					}
					else
					{
						childlist = tempport->next;
						tempport->Release();
						tempport = childlist;
					};
					retval = false;
				}
				else
				{
					outport = tempport;
					tempport = tempport->next;
				};
			};
		};
			break;

		// unsupported dispatch message type
		default:
			ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "Invalid Dispatch Message opcode" );
			break;
	};

	OUTFUNCRET( retval );
};



bool XCGCS::HandleTransactionMessage( XCMsgPort* port, XCToolsMessage* msg ) {
	INFUNC( XCGCS::HandleTransactionMessage, this );

	bool retval = true;

	trans_query_base* trans = (trans_query_base*)msg->GetDataPtr();
	base_message* base = msg->GetHeaderPtr();

	try {
//		printf( "HANDLE TRANS MESSAGE: %d\n", trans->appnbr );

		// Determine what to do with the transaction
		if (trans->appnbr == GCS_APP_NUMBER) {
			base_message* base = msg->GetHeaderPtr();
			base->sdflag |= dest_gcs;
			retval = XCAppInterface::HandleTransactionMessage( port, msg );
		} else if (base->sdflag & dest_app) {
			// Check for previous traffic
			XCGCSTransaction* previous = hostlist.GetTransList()->Find( trans->transid );
			if (!previous) {
				// Load balance here...
				previous = hostlist.GetTransList()->Add( trans->transid );
				previous->host = hostlist.LoadBalance( trans->appnbr );
			};

			if (previous->host) {
				ForwardMessage( port, msg, previous->host->owner->msg_port );
			} else {
				printf( "Can't find application: %ld\n", trans->appnbr );
				RAISETOOLSERROR( ERR_NO_APPLICATION );
			};
		} else if (base->sdflag & dest_int) {
			route_info* route = msg->PeekLastRoute();

			if (route->src) {
				// Likely a transaction result
				if (trans->data_type == TRANS_HEADER_INFO) {
					trans_message* tm = (trans_message*)msg->GetDataPtr();

					// Special case for a failed login transaction
					if (tm->format == -1) {
						// Fill in information specific to the info message
						trans_info_message* imsg = (trans_info_message*)(tm->data);
						if (!imsg->success) {
							// Remove session
							licensing.RemoveSession( imsg->data, imsg->session_id );
						};
					};
				};

				// Remove from the transaction list
				hostlist.GetTransList()->Remove( trans->transid );
				ForwardMessage( port, msg );
			};
		} else if (base->sdflag & dest_gcs) {
			// Message for gcs...  Likely a transaction result
			retval = XCAppInterface::HandleTransactionMessage( port, msg );
		} else {
			// Most likely a transaction response destined for the interface
			ForwardMessage( port, msg );
		};
	} catch( XCToolsError& err ) {
		// Only send things back if this is a TRANS HEADER record
		if (trans->data_type == TRANS_HEADER_INFO) {
			// Trick to fix things?
			base_message* base = msg->GetHeaderPtr();
			base->sdflag = (base->sdflag & ~dest_all) | dest_gcs;

			const char* errstr = err.GetDescription();
			ToolsTrace( TRC_APPINTERFACE, TLV_LOW, "EXCEPTION: %s  [TransID=%d]", errstr, trans->transid );

			XCTransactionInfo* txnreply = new XCTransactionInfo( this, port );
			txnreply->SetSuccess( false );
			txnreply->SetTransID( trans->transid );

			// Fill in information specific to the info message
			trans_message* tm = (trans_message*)msg->GetDataPtr();
			trans_info_message* imsg = (trans_info_message*)(tm->data);

			txnreply->SetTransNumber( tm->format );
			txnreply->SetPhase( imsg->phase );

			TransResponseMsg( txnreply, err.GetErrorNumber(), err.GetDescription(), 0, 0, SEV_CRITICAL );
			SendTransaction( txnreply, port, msg );

			// clean up...
			txnreply->Release();

			// Send notice to driver to cleanup transaction fragment, if needed
			XCGCSTransaction* previous = hostlist.GetTransList()->Find( trans->transid );
			if (previous) {
				// If we had a previous host, send a message to destroy any fragments
				if (previous->host) {
					XCToolsMessage disp( this );
					disp.CreateMsg( DISP_MESSAGE_SIZE + sizeof( long ) );
					disp.SetHeader( msg_dispatch, src_gcs, dest_app, false, false );

					disp_message* dmsg = (disp_message*)disp.GetDataPtr();
					dmsg->appnbr = trans->appnbr;
					dmsg->opcode = disp_abort_txn;
					memcpy( dmsg->data, &trans->transid, sizeof( long ) );

					SendMessage( msg_src, previous->host->owner->msg_port, &disp );
				};

				// Remove the app/trans assocation
				hostlist.GetTransList()->Remove( trans->transid );
			};
		};
	};

	OUTFUNCRET( retval );
};



// Overloaded XCAppInterface::GotMessage callback function
bool XCGCS::GotMessage( XCMsgPort* port, XCToolsMessage* msg )
{
	INFUNC( XCGCS::GotMessage, this )

	bool retval = true;
	XCAppInterface::msg_recv++;

	ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "GCS Received Message : %ld on Port : %ld", msg->GetHeaderPtr()->type, port->transport->GetInHandle() );
	msg->Dump();

	switch( msg->GetHeaderPtr()->type ) {
		case msg_user:
			break;

		case msg_options:
			// Forward all Transactions/options to the parent class
			retval = XCAppInterface::GotMessage( port, msg );
			break;

		case msg_trans:
		case msg_sql_query:
			// Check for GCS transactions...
			retval = HandleTransactionMessage( port, msg );
			break;

		case msg_admin:
			retval = LogAdminMsg( (admin_message*)msg->GetDataPtr() );
			break;

		case msg_status:
			retval = HandleStatusMsg( (status_message*)msg->GetDataPtr(), port );
			break;

		// dispatcher messages
		case msg_dispatch: 
			retval = HandleDispatchMessage( port, msg );
			break;

		// unsupported message type
		default:
			ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "Invalid message type" );
			break;
	};

	OUTFUNCRET( retval )
};






void XCGCS::OnInit() {
	// Load our xml information to determine required tracing, etc...
	want_apps = false;
	if (!LoadParms())
		exit( 1 );

	// Set Tracing Options...
	XCAppOptions myopts;

	GetOptions( myopts );
	myopts.debug_type_filter = gcs.debug_type_filter;
	for (int i=0; i<num_tc; i++) {
		myopts.debug_level_cutoff[ i ] = gcs.debug_level_cutoff[ i ];
	};
//	myopts.no_database = true;
	SetOptions( myopts );

	auth_method = (AuthenticateMethod)gcs.auth_method;
	auth_parameter = gcs.auth_parameter;
	worker_threads = gcs.worker_threads;

	Initialize( "gcs", GCS_APP_NUMBER );	// -1 signifies that we want to load SAI's buffers in addition to the system stuff
};


void XCGCS::GCSMessage( int msg_nbr ) {
	char* error_msg = GetCatalogMsg( msg_nbr, 0 );
	if (error_msg) {
		admin_message* msg = (admin_message*)new char[ sizeof( admin_message ) + strlen( error_msg ) ];
		msg->err_num = msg_nbr;
		msg->field_num = 0;
		msg->src_pid = 0;
		sprintf( msg->err_message, error_msg );
		free( error_msg );

		LogAdminMsg( msg );
		delete msg;
	} else {
		DEBUGOUT( "Unable to obtain message to write to GCS log" );
	};
};



void XCGCS::OnRun() {
	INFUNC( XCGCS::OnRun, this );

	// Start the timer
	timer.Start();

	// Start the listener port
	MainFunc( gcs.port );

	// Start the local substation and the boot process
	StartUp();

	// Start the scheduler thread
	THREAD_HANDLE scheduler = XCCreateThread( SchedulerThread, this  );

	// Write a log entry that the system start
	GCSMessage( ERR_SYSTEM_START );

	// handle all messages
	MessageLoop( dest_gcs );
	ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "GCS Stopped." );

	// Shutdown the scheduler thread
	XCJoinThread( scheduler );

	// Write a log entry that the system has stopped
	GCSMessage( ERR_SYSTEM_STOP );

	OUTFUNC();
};

void XCGCS::OnCleanup() {
};

void XCGCS::OnSignalStop() {
	QuitLoop();
};


void XCGCS::OnDroppedConnection( XCMsgPortPtr tempport ) {
	printf( "GCS detected connection loss!\n" );

	// Check to see if it was a substation...
	XCSubstation* substation = hostlist.FindHost( tempport );
	if (substation) {
		printf( "Drop substation %s\n", substation->host );
		hostlist.RemoveHost( substation );
	} else {
		// Maybe it's a user-interface
		XCInterface* temp = hostlist.GetFirstInterface();
		while (temp && temp->msg_port != tempport)
			temp = temp->next;

		if (temp) {
			printf( "Drop interface\n" );
			hostlist.RemoveInterface( temp );
		};
	};

	// Are we attempting to shutdown?
	if (shutdown) {
		// Do we have any outstanding interfaces or programs?
		if (!hostlist.GetFirstInterface() && !hostlist.GetFirstHost()) {
			// Nobody left, abort
			QuitLoop();
		};
	};
};







void XCGCS::StartInterfaces()
{
	INFUNC( XCGCS::StartInterfaces, this );

	char* buffer = new char[ FILENAME_MAX ];
	char* hostname = new char[ FILENAME_MAX ];
	char* parms[5];
	XCMsgPortPtr subchild;

//	strcpy( hostname, "localhost" );
	XCGetHostName( hostname, FILENAME_MAX );

	XCStartInterface* tempint = interfacelist;

	char port[ 20 ];
	char temp[ 20 ];
	char tracing[ 20 ];
	sprintf( temp, "%ld", gcs.port );
	parms[0] = hostname;	// host name
	parms[1] = temp;		// gcs listener port
	parms[2] = port;
	parms[3] = tracing;
	parms[4] = NULL;		// terminate the list...

	if (!tempint) {
		printf( "No interfaces to start!\n" );
	};

	while ( tempint )
	{
		if (!running_as_service) {
			// setting up parameters to start a new substation
			strcpy( buffer, tempint->name );
			sprintf( port, "%ld", tempint->port );
			sprintf( tracing, "%d", tempint->tracing );

			ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "GCS Create Process : %s", buffer );

			try {
				subchild = NULL;
				subchild = CreateChild( buffer, 4, parms, false );
			} catch( ... ) {
				cout << "Unable to start interface: " << tempint->name << " on port: " << tempint->port << endl;
			};

			// Set the childs port type
			if ( subchild )
				subchild->porttype = pt_sub;
			else
				ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "GCS Create Process Failed." );
		} else {
			// Use the services manager to start it
			service_daemon.StartRemoteService( tempint->machine, tempint->name );
		};

		tempint = tempint->next;
	};
	
	// Cleanup
	delete [] hostname;
	delete [] buffer;

	OUTFUNC();
};

//@ SEC 3 
//@ CHA Save Config
//@ COD SaveConfig
void XCGCS::SaveConfig()
{
	char* config = new char[ 1024 * 1024 ];
/*		
	//@ Save Headers
	sprintf( config, "%s\n<%s %s=\"1.0\">\n", xmltag, excaliburtag, versiontag );

	//@ Save GCS Parameters
	if ( new_listen_port )
		sprintf( config, "%s\t<%s %s=\"%s\" />\n", config, gcstag, porttag, new_listen_port );
	else
		sprintf( config, "%s\t<%s %s=\"%s\" />\n", config, gcstag, porttag, listen_port );

	//@ Save all Applications and Parameters
	sprintf( config, "%s\t<%s>\n", config, applisttag );
	XCStartApp* tempapp = applist;
	while ( tempapp )
	{
		sprintf( config, "%s\t\t<%s %s=\"%ld\" %s=\"%s\" %s=\"%s\" />\n", config, apptag, numbertag, tempapp->number, nametag, tempapp->name, machinetag, tempapp->machine );
		tempapp = tempapp->next;
	};
	sprintf( config, "%s\t</%s>\n", config, applisttag );

	//@ Save all Interfaces and Parameters
	sprintf( config, "%s\t<%s>\n", config, interfacelisttag );
	XCStartInterface* tempint = interfacelist;
	while ( tempint )
	{
		sprintf( config, "%s\t\t<%s %s=\"%s\" %s=\"%s\" %s=\"%ld\" />\n", config, interfacetag, nametag, tempint->name, machinetag, tempint->machine, porttag, tempint->port );
		tempint = tempint->next;
	};
	sprintf( config, "%s\t</%s>\n", config, interfacelisttag );

	//@ Save Footers
	sprintf( config, "%s</%s>\n", config, excaliburtag );

	//@ Save complete file
	XCFileTransport conffile( this );
	conffile.Open( "gcs.xml", write_only, create_always );
	conffile.Send( config, strlen( config ) );
	conffile.Close();
*/
	// Clean up
	delete[] config;
};

//@ CHA Set Port
//@ COD SetPort
void XCGCS::SetPort( const char* port )
{
	//@ Remove old port number
	if ( new_listen_port ) 
		delete[] new_listen_port;
	//@ Set new port number
	new_listen_port = strdup( port );
	cout << "!!!!!!!!!!!!!! New GCS Listener Port : " << new_listen_port << " !!!!!!!!!!!!!!!!!!!!" << endl;
};





THREAD_PROCEDURE XCGCS::SchedulerThread( void* parm ) {
	XCGCS* app = (XCGCS*)parm;
	assert( app );

	int sleep_time = 30;

	XC_WAIT_RESULT result;
	do {
		result = app->GetAbortEvent()->Wait( sleep_time );
		if (result == XCWAIT_TIMEOUT) {
			try {
				app->ExecuteJobs();
			} catch( XCToolsError& ex ) {
				printf( "TOOLS ERROR: %s\n", ex.GetDescription() );
			} catch( ... ) {
				printf( "ERROR EXECUTING JOBS\n" );
			};
		};
	} while (result == XCWAIT_TIMEOUT);

	return 0;
};



// Define the Control Handler
GCS_CTRL_HANDLER;


//////////////////////////////////////////
// main function
//
// Usage : gcs [install/uninstall]
int main( int argc, char** argv )
{
	Protea::ShowIntro( "gcs" );

	if (argc > 1 && !strcmp(argv[1],"-L"))
	{
		Protea::ShowLicense();
		return 0;
	};

	if (!setlocale( LC_ALL, "" )) {
		fprintf( stderr, "Locale not specified. Check LANG, LC_CTYPE, LC_ALL." );
		return 1;
	};

	// When running gcs, we should set the current directory to the dir where gcs is running
	char* dir = strdup( argv[0] );
	char* temp = dir;
	char* last = temp;
	while (temp) {
		temp = strstr( temp, OSFILEDELIM );
		if (temp) {
			last = temp + 1;
			temp++;
		};
	};
	*last = 0;
	if (*dir)
		XCChDir( dir );
	free( dir );

	Protea::Initialize();

	bool state = true;
	try {
		INFUNC( main, NULL )

		DO_TRAFFIC_SECURITY_CHECK;

		if (argc == 2) {
			if (strcmp( argv[1], "install" ) ==0) {
				gcs.Install();
				RETURNFUNC( 0 );
			} else if (strcmp( argv[1], "uninstall" )==0) {
				gcs.Uninstall();
				RETURNFUNC( 0 );
			} else if (strcmp( argv[1], "noservice" )==0) {
				state = false;
			};
		};

		// Register our signal handlers
		GCS_SIG_HANDLER;
		
		// Run the daemon
		service_daemon.SetService( state );
		service_daemon.Add( &gcs );
		service_daemon.Register();

		OUTFUNC()
	} catch( XCToolsError& error ) {
		XCOutputCallStack( &gcs, error.GetErrorNumber(), error.GetDescription() );
	} catch( ... ) {
		XCOutputCallStack( &gcs, -1, NULL );
	};

	if ( new_listen_port )
		delete[] new_listen_port;

	Protea::Uninitialize();

	return 0;
};
